package com.rc.web.server;

import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaFoxUtil;
import com.rc.common.encoder.MessageDecoder;
import com.rc.common.model.MessageModel;
import com.rc.common.util.JsonUtil;
import com.rc.common.util.RedisHelper;
import com.rc.pojo.po.Consumer;
import com.rc.pojo.po.Farm;
import com.rc.pojo.po.Game;
import com.rc.pojo.po.User;
import com.rc.web.mapper.FarmMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * WebSocket服务端实现类
 * 处理WebSocket连接、消息收发等操作
 */
@Slf4j
@Component
@ServerEndpoint(value = "/ws/{token}",decoders = {MessageDecoder.class})
public class WebSocketServer {

    // 静态变量用于依赖注入
    private static FarmMapper farmMapper;
    private static RedisHelper redisHelper;
    
    // 存储用户ID与Session的映射关系
    private static final Map<Long, Session> sessionMap = new ConcurrentHashMap<>();

    // 无参构造函数
    public WebSocketServer() {
    }

    /**
     * 静态方法注入依赖
     * @param farmMapper 农场Mapper
     * @param redisHelper Redis工具类
     */
    @Autowired
    public void setDependencies(FarmMapper farmMapper, RedisHelper redisHelper) {
        WebSocketServer.farmMapper = farmMapper;
        WebSocketServer.redisHelper = redisHelper;
    }

    /**
     * WebSocket连接建立时触发
     * @param session WebSocket会话
     * @param token 用户token
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token) {
        // 从token中获取用户ID
        var loginId = SaFoxUtil.getValueByType(StpUtil.getLoginIdByToken(token), Long.class);

        // 获取并发送所有离线消息
        var cacheName = getCacheName(loginId);
        var offlineMessageList = redisHelper.<MessageModel>getCacheList(cacheName);
        offlineMessageList.forEach(offlineMessage -> sendMessage(session,offlineMessage));
        redisHelper.deleteObject(cacheName);

        // 将用户ID与Session存入Map
        sessionMap.put(loginId,session);
        log.info("WebSocket 连接已经建立:链接数量{}",sessionMap.size());
    }

    /**
     * 接收到客户端消息时触发
     * @param message 消息对象
     * @param token 用户token
     */
    @OnMessage
    public void onMessage(MessageModel message,@PathParam("token") String token) {
        // 从token中获取用户ID
        var loginId = SaFoxUtil.getValueByType(StpUtil.getLoginIdByToken(token), long.class);
        message.setFrom(loginId);

        // 设置发送者信息
        var consumer = StpUtil.getSessionByLoginId(loginId).getModel("consumer", Consumer.class);
        if (consumer != null) {
            message.setAvatar(consumer.getAvatar());
            message.setNickname(consumer.getNickName());
        } else {
            User user = StpUtil.getSessionByLoginId(loginId).getModel("user", User.class);
            Farm farm = farmMapper.selectOneById(user.getFarmId());
            message.setNickname(farm.getName());
            message.setAvatar(user.getAvatar());
        }

        log.info("收到客户端消息：" + message);

        // 处理消息发送
        var toId = message.getTo();
        var toSession = sessionMap.get(toId);

        if (toSession != null){
            // 对方在线则直接发送
            sendMessage(toSession,message);
        }else{
            // 不在线则存入离线消息
            String cacheName = getCacheName(toId);
            redisHelper.setCacheList(cacheName,List.of(message));
        }
    }

    /**
     * WebSocket连接关闭时触发
     * @param token 用户token
     * @param session WebSocket会话
     */
    @OnClose
    public void onClose(@PathParam("token") String token,Session session) throws IOException {
        var loginId = SaFoxUtil.getValueByType(StpUtil.getLoginIdByToken(token), Long.class);
        sessionMap.remove(loginId);
        session.close();
        log.info("WebSocket 连接已经关闭。");
    }

    /**
     * WebSocket发生错误时触发
     * @param t 异常对象
     */
    @OnError
    public void onError(Throwable t) {
        t.printStackTrace();
    }

    /**
     * 发送消息给指定会话
     * @param session WebSocket会话
     * @param message 消息对象
     */
    private void sendMessage(Session session,MessageModel message) {
        try {
            String response = JsonUtil.toJson(message);
            session.getBasicRemote().sendText(response);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取离线消息缓存名称
     * @param id 用户ID
     * @return 缓存名称
     */
    private static String getCacheName(Long id){
        return "offlineMessage:" + id;
    }
}
